﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Tools.SubStream;
using System.Collections;
using System.Threading;
using System.Security.Cryptography;
using Tools.Debugging;

namespace Tools.File.Hashing {
    public class FileHasherSettings {

        public FileHasherSettings ( ) { }
        public static FileHasherSettings Default {
            get {
                return new FileHasherSettings( );
            }
        }
    }

    public class HashingSettings {
        public int BlockLength = 1024*16;
        public int ConcurentHashingThreads = 5;
    }

    public class HashFragment {
        public Range Range;
        public String Name;
        public MuxerSubStream Stream;
        public String Data;


    }

    public struct WorkerData {
        public HashFragment Fragment;
        public EventWaitHandle WaitHandle;
    }

    public class FileHasher {
        protected FileHasherSettings settings;
        protected StreamMuxer stream;
        private FileHasher ( StreamMuxer stream ) {

        }

        public FileHasher ( String fileName ) {
            this.stream = new StreamMuxer( System.IO.File.OpenRead( fileName ) );
        }

        public FileHasher ( String fileName, FileHasherSettings settings ) {
            this.Settings = settings;
            this.stream = new StreamMuxer( System.IO.File.OpenRead( fileName ) );
        }


        public FileHasherSettings Settings {
            set {
                this.settings = value;
            }
            get {
                if ( this.settings == null ) {
                    this.settings = FileHasherSettings.Default;
                }
                return this.settings;
            }
        }
        protected List<HashFragment> fragmentList = new List<HashFragment>( );

        public List<HashFragment> FragmentList {
            get {
                return this.fragmentList;
            }
        }

        protected void HashWorker ( Object o ) {
            WorkerData data = ( WorkerData ) o;
            HashFragment fragment = data.Fragment;
            MD5 hasher = MD5.Create( );
            hasher.ComputeHash( fragment.Stream );
            StringBuilder sb = new StringBuilder( );
            foreach ( byte b in hasher.Hash ) {
                sb.AppendFormat( "{0:x2}", b );
            }
            fragment.Data = sb.ToString( );
            //data.WaitHandle.Set( );
            sema.Release( );
        }

        Semaphore sema;

        public void Hash ( HashingSettings settings ) {
            int length = settings.BlockLength;
            //List<EventWaitHandle> waitersList = new List<EventWaitHandle>( );
            this.fragmentList.Clear( );
            sema = new Semaphore( 0, settings.ConcurentHashingThreads );
            int i = -1;

            do {
                i++;

                HashFragment fragment = new HashFragment( );
                this.fragmentList.Add( fragment );
                fragment.Range = new Range( i * length, length );
                fragment.Name = "sub" + i;
                fragment.Stream = this.stream.AddSubStream( fragment.Range, fragment.Name );
                //Logger.Debug(this,  "r:{0}, n:{1}, sl:{2}", fragment.Range, fragment.Name, fragment.Stream.Length );

                Thread t = new Thread( new ParameterizedThreadStart( HashWorker ) );
                //ManualResetEvent wevnt = new ManualResetEvent( false );
                //waitersList.Add( wevnt );

                t.Start( new WorkerData( ) { Fragment = fragment/*, WaitHandle = wevnt*/ } );
                sema.WaitOne( );
            } while ( i * length + length < this.stream.Length );

            //EventWaitHandle.WaitAll( waitersList.ToArray( ) );

        }
    }
}
